#include <ft2build.h>
#include FT_FREETYPE_H 

#include "font.h"
#include <malloc.h>

Font* defaultFont;

FT_Library library;

void FontInit()
{
	if ( FT_Init_FreeType( &library ) )
	{
		printf("error during FT_Init_FreeType\n");
	}
}

void FontDone()
{
	FT_Done_FreeType(library);
}

void enableFont()
{
	GX_SetVtxDesc (GX_VA_CLR0, GX_NONE);
	GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT);
	GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP1, GX_COLORZERO);
}

void plotPixelRGBA8(u8* texBuffer, int bufWidth, int x, int y, u8 opacity)
{
	int cellY = y/4;
	int cellX = x/4;
	u16* cellPtr = &(((u16*)texBuffer)[cellY*4*bufWidth*2+cellX*4*4*2]);
	int newX = x%4;
	int newY = y%4;
	cellPtr[ (newY*4 + newX) ]  = (opacity<<8) + 0xFF;	// AR

	cellPtr[ 4*4 + (newY*4 + newX)  ]  = 0xFFFF;		// GB
}

void plotPixelIA8(u8* texBuffer, int bufWidth, int x, int y, u8 opacity)
{
	int cellY = y/4;
	int cellX = x/4;
	u16* cellPtr = &(((u16*)texBuffer)[cellY*4*bufWidth+cellX*4*4]);
	int newX = x%4;
	int newY = y%4;
	cellPtr[ (newY*4 + newX) ]  = (opacity<<8) + 0xFF;	// AI
}

Font::Font(const void* fontBuffer, unsigned long fontBufferSize, unsigned int size)
{
	charHeight = size;
	FT_Face face; /* handle to face object */ 
	if ( FT_New_Memory_Face( library, static_cast<const FT_Byte*>(fontBuffer), fontBufferSize, 0, &face )==0 )
	{
		unsigned int emSize = charHeight*face->units_per_EM/face->height;
		if( FT_Set_Pixel_Sizes( face, 0, emSize) !=0 ) return;
		max_advance_width = (face->max_advance_width)*emSize/(face->units_per_EM);

		int texWidth= max_advance_width*16;
		int texHeight = charHeight*16;

		texture = reinterpret_cast<u8*>(memalign(32,texWidth*texHeight*2));
//		texture = reinterpret_cast<u8*>(memalign(32,texWidth*texHeight*4));

		memset(texture, 0x00, texWidth*texHeight*2);
//		memset(texture, 0x00, texWidth*texHeight*4);

		int penX = 0;
		int baseline = (face->ascender)*emSize/(face->units_per_EM);
		for(int idx=0; idx!=0x100; ++idx)
		{
			FT_UInt glyph_index = FT_Get_Char_Index( face, idx);
			if(glyph_index 
			&& FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT )==0
			&& FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL )==0)
			{
//				charWidth[idx] = face->glyph->bitmap.width;
//				charWidth[idx] = (face->glyph->advance.x)*emSize/(face->units_per_EM);
				charWidth[idx] = (face->glyph->linearHoriAdvance)>>16;

//				int charY = baseLine-face->glyph->bitmap_top;
				int charY = (idx/16)*charHeight+baseline-(face->glyph->bitmap_top);
//				int charX = penX+face->glyph->bitmap_left;
				penX = (idx%16)*max_advance_width;// + (face->glyph->bitmap_left);
				unsigned char *ptr = face->glyph->bitmap.buffer;
				for(int y=0; y<(face->glyph->bitmap.rows); ++y)
				{
					for(int x=0; x<(face->glyph->bitmap.width); ++x)
					{
						plotPixelIA8(texture, texWidth, /*charX+*/penX+x, charY+y, ptr[x]);
//						plotPixelRGBA8(texture, texWidth, /*charX+*/penX+x, charY+y, ptr[x]);
					}
					ptr+=(face->glyph->bitmap.pitch);	
				}
//				penX+=max_advance_width;
			}
//			penX+=charWidth[idx];
		}
	FT_Done_Face(face);

		DCFlushRange(texture,texWidth*texHeight*2);
//		DCFlushRange(texture,texWidth*texHeight*4);

		GX_InitTexObj(&texObj, MEM_K0_TO_K1((void*)texture), texWidth, texHeight, GX_TF_IA8, GX_CLAMP, GX_CLAMP, GX_FALSE);
//		GX_InitTexObj(&texObj, MEM_K0_TO_K1((void*)texture), texWidth, texHeight, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE);
		GX_LoadTexObj(&texObj, GX_TEXMAP1);
	}
}

Font::~Font()
{
	free(texture);
}

float Font::drawChar( int x1, int y1, char character, int height)
{
	float width = height*charWidth[character]/((float)charHeight);
	float s1 = (character%16+0)/16.0f;
	float s2 = s1+(charWidth[character]/((float)(max_advance_width*16)));
	float t1 = (character/16+0)/16.0f;
	float t2 = (character/16+1)/16.0f;
	float x2 = x1+width;
	float y2 = y1+height;

//---------------------------------------------------------------------------------
	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);			// Draw A Quad
		GX_Position2f32(x1, y1);	// Top Left
		GX_TexCoord2f32(s1, t1);

		GX_Position2f32(x2, y1);	// Top Right
		GX_TexCoord2f32(s2, t1);

		GX_Position2f32(x2, y2);	// Bottom Right
		GX_TexCoord2f32(s2, t2);

		GX_Position2f32(x1, y2);	// Bottom Left
		GX_TexCoord2f32(s1, t2);
	GX_End();						// Done Drawing The Quad 
	return width;
}

int Font::drawText( int x, int y, const char* str, int height)
{
	enableFont();
	int retVal =0;
	float xPos = x;
	while(*str!='\0' && *str!='\n')
	{
		xPos += drawChar(xPos, y, *str, height);
		retVal++;
		str++;
	}
	return retVal;
}
